<?php
/**
 *
 *
 */
class Yz_Module_Router
    extends Yz_Class
    implements Yz_Module_Router_Interface
{

    const DEFAULT_CONTROLLER = "Default";
    const ERROR_CONTROLLER = false;

    protected $module;


    /**
     *  @param Yz_Module_Interface $module
     */
    public function __construct( Yz_Module_Interface $module )
    {
        debug();
        $this->module = $module;
    }

    /**
     *
     */
    public function route(Yz_Array $args, Yz_Hash $post=null)
    {
        $ctlr = $this->identifyController($args);

        try {
            return $this->execController($ctlr, $args, $post);
        } catch (Error $ex) {
            try {
                $r = $ctlr->doError($ex);
            } catch (Error $ex) {
                // if ctlr's error handler throws exception,
                // it will be handled
                $r = false;
            }
            return $r ?: $this->routeError($ex);
        }
    }



    /**
     *
     */
    public function routeError(Error $ex)
    {
        $ctlr = $this->selectErrorController();
        if (! $ctlr)
            throw $ex;
        return $ctlr->doError($ex);
    }


    /**
     *  @return Yz_Controller_Interface
     */
    protected function execController($ctlr, $args, $post)
    {

        if ($post) {
            $r = $ctlr->doPost($args, $post);
        } else {
            $r = $ctlr->doGet($args);
        }
        switch (true) {
            case false === $r:
                // restart routing process
                return false;
            case is_string($r):
                // select controller by the name $r
                $ctlr = $this->selectController($r);
                return $this
                    ->execController($ctlr, $args, $post)
                ;
            default:
        }
        assert('$r instanceof Yz_Controller_Interface');
        return $r;
    }


    /** find a controller by exact name
     *  @return Yz_Controller_Interface&
     *  @throws ClassError
     */
    protected function selectController($ctlrName)
    {
        debug($ctlrName);
        return $this->module->selectController($ctlrName);
    }



    /** Select a default controller (usually, when a named controller not found)
     *      It may be an error controller ("controller $name not found")
     *      or module contents generator
     *      or a dispatcher
     *  @return Yz_Controller_Interface&
     *  @throws NotImplementedError
     */
    protected function selectDefaultController()
    {
        $ctlrName = static::DEFAULT_CONTROLLER;
        if (! $ctlrName)
            throw NotImplementedError::_new_();
        return $this->selectController($ctlrName);
    }

    protected function selectErrorController()
    {
        $ctlrName = static::ERROR_CONTROLLER;
        if (! $ctlrName)
            return false;
        return $this->selectController($ctlrName);
    }

    /**
     *  @return Yz_Controller_Interface
     */
    protected function identifyController(Yz_Array $args)
    {
        $ctlr = null;
        if (! $args->isEmpty()) {
            $ctrlName = Yz_String::toCamelCase($args[0]);
            if (Yz_String::isValidIdentifier($ctrlName)) {
                try {
                    $ctlr = $this->selectController($ctrlName);

                    // real selector
                    $ctlrSelector = $args->shift() .'/';
                } catch (ClassError $ex) {
                    //
                }
            }
        }
        if (! $ctlr)
            $ctlr = $this->selectDefaultController();
        return $ctlr;
    }

} // class
